﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TableParsers;

using DBL = DatabaseLayer;

namespace Speakup
{
    public class WeekTheme: IEquatable<WeekTheme>, IComparable, IComparable<WeekTheme>
    {
        public static Dictionary<int, ClassType> DBClassType2SpeakupClassType = new Dictionary<int, ClassType> 
        {
            {1, ClassType.Grammar},
            {2, ClassType.Conversation},
            {3, ClassType.Reading},
            {4, ClassType.Writing},
        };

        private static ClassType ClassTypeFromDB(int dbClassTypeId, string dbClassTypeName)
        {
            if (!DBClassType2SpeakupClassType.Keys.Contains(dbClassTypeId))
            {
                //try to match by name.
                if (dbClassTypeName != null)
                {
                    Type ctType = typeof(ClassType);
                    foreach (var value in Enum.GetValues(ctType))
                    {
                        string name = Enum.GetName(ctType, value);
                        if (name == dbClassTypeName)
                        {
                            return (ClassType)value;
                        }
                    }
                }
                throw new ArgumentException("DB ClassType is not recognized.");
            }

            return DBClassType2SpeakupClassType[dbClassTypeId];
        }
        private static ClassType ClassTypeFromDB(int dbClassType)
        {
            return ClassTypeFromDB(dbClassType, null);
        }

        public static List<ClassType> SupportedClassTypes() 
        {
            using (var dbCtx = DBL.DatabaseContextFactory.Create())
            {
                return SupportedClassTypes(dbCtx);
            }
        }
        public static List<ClassType> SupportedClassTypes(DBL.SpeakupEntities dbCtx) 
        {
            var dbClassTypes = (from n in dbCtx.ClassTypes
                               select n).ToList();

            return (from n in dbClassTypes
                    select ClassTypeFromDB(n.Id, n.Name)).ToList();

        }

        public static List<int> SupportedWeeks() 
        {
            using (var dbCtx = DBL.DatabaseContextFactory.Create())
            {
                return SupportedWeeks(dbCtx);
            }
        }
        public static List<int> SupportedWeeks(DBL.SpeakupEntities dbCtx)
        {
            return (from n in dbCtx.Weeks
                    select n.Id).ToList();
        }

        public static List<ThemeLevel> SupportedThemeLevels() 
        {
            using (var dbCtx = DBL.DatabaseContextFactory.Create())
            {
                return SupportedThemeLevels(dbCtx);
            }
        }        
        public static List<ThemeLevel> SupportedThemeLevels(DBL.SpeakupEntities dbCtx)
        {
            var dbLevels = (from n in dbCtx.Levels
                           select n).ToList();


            return (from n in dbLevels
                    select new ThemeLevel(n.MinLevel, n.MaxLevel)).ToList();
        }
        

        public delegate bool ThemeFilter(ThemeLevel level, ClassType classType, int week);

        public static ThemeFilter LevelFilter(int level)
        {
            return (lvl, ct, w) =>
            {
                return lvl.ContainsLevel(level);
            };
        }
        public static ThemeFilter LevelFilter(ThemeLevel level)
        {
            return (lvl, ct, w) =>
            {
                return lvl == level;
            };
        }
        public static ThemeFilter WeekFilter(int week)
        {
            return (lvl, ct, w) =>
            {
                return w == week;
            };
        }
        public static ThemeFilter ClassTypeFilter(ClassType classType)
        {
            return (lvl, ct, w) =>
            {
                return ct == classType;
            };
        }
        public static ThemeFilter ClassTypeAndWeekFilter(ClassType classType, int week)
        {
            return (lvl, ct, w) =>
            {
                return ct == classType && w == week;
            };
        }
        public static ThemeFilter ClassTypeAndLevelFilter(int level, ClassType classType)
        {
            return (lvl, ct, w) =>
            {
                return ct == classType && lvl.ContainsLevel(level);
            };
        }
        public static ThemeFilter ClassTypeAndLevelFilter(ThemeLevel level, ClassType classType)
        {
            return (lvl, ct, w) =>
            {
                return ct == classType &&
                       lvl == level;
            };
        }
        public static ThemeFilter WeekAndLevelFilter(int level, int week)
        {
            return (lvl, ct, w) =>
            {
                return w == week && lvl.ContainsLevel(level);                       
            };
        }
        public static ThemeFilter WeekAndLevelFilter(ThemeLevel level, int week)
        {
            return (lvl, ct, w) =>
            {
                return w == week && lvl == level;
            };
        }
        public static ThemeFilter ByAllFilter(ClassType classType, int level, int week)
        {
            return (lvl, ct, w) =>
            {
                return lvl.ContainsLevel(level) && ct == classType && w == week;
            };
        }
        public static ThemeFilter ByAllFilter(ClassType classType, ThemeLevel level, int week)
        {
            return (lvl, ct, w) =>
            {
                return lvl == level && ct == classType && w == week;
            };
        }

        static public List<WeekTheme> GetWeekThemes(ThemeFilter filter)
        {
            using (var dbCtx = DBL.DatabaseContextFactory.Create())
            {
                return GetWeekThemes(filter, dbCtx);
            }
        }
        static public List<WeekTheme> GetWeekThemes(ThemeFilter filter, DBL.SpeakupEntities dbCtx)
        {
            var dbThemes = (from n in dbCtx.Schedule
                            select new
                            {
                                ClassType = n.ClassTypeId,
                                ClassTypeName = n.ClassType.Name,
                                MinLevel = n.Level.MinLevel,
                                MaxLevel = n.Level.MaxLevel,
                                Week = n.WeekId,
                                Theme = n.Theme
                            }).ToList();


            var result = from n in dbThemes
                         where filter(new ThemeLevel(n.MinLevel, n.MaxLevel),
                                     ClassTypeFromDB(n.ClassType, n.ClassTypeName),
                                     n.Week)
                         select new WeekTheme(ClassTypeFromDB(n.ClassType, n.ClassTypeName),
                                              new ThemeLevel(n.MinLevel, n.MaxLevel),
                                              n.Week,
                                              n.Theme);

            return result.ToList();             
        }


        WeekTheme()
        {
            Theme = null;            
        }

        public WeekTheme(WeekThemeKey key, string theme)
        {
            Key = key;
            Theme = theme;
        }

        public WeekTheme(ClassType classType, ThemeLevel level, int week, string theme)
        {
            Key = new WeekThemeKey(classType, level, week);            
            Theme = theme;
        }

        public override bool Equals(object obj)
        {
            WeekTheme rhs = obj as WeekTheme;
            if (rhs == null) return false;

            return Equals(rhs);
        }
        public bool Equals(WeekTheme rhs)
        {
            if (ReferenceEquals(this, rhs))
                return true;

            return Level == rhs.Level &&
                   ClassType == rhs.ClassType &&
                   Week == rhs.Week &&
                   Theme == rhs.Theme;
        }
        public override int GetHashCode()
        {
            int hash = 13;
            unchecked
            {
                hash = (hash * 397) + Level.GetHashCode();
                hash = (hash * 397) + ClassType.GetHashCode();
                hash = (hash * 397) + Week.GetHashCode();
                hash = (hash * 397) + Theme.GetHashCode();
            }
            return hash;            
        }
        public int CompareTo(object obj)
        {
            return CompareTo((WeekTheme)obj);
        }
        public int CompareTo(WeekTheme rhs)
        {
            if (ClassType != rhs.ClassType)
            {
                return ClassType.CompareTo(rhs.ClassType);         
            }

            if (Level != rhs.Level)
            {
                return Level.CompareTo(rhs.Level);
            }

            if (Week != rhs.Week)
            {
                return Week.CompareTo(rhs.Week);
            }

            return Theme.CompareTo(rhs.Theme);
        }
        
        public override string ToString()
        {
            return Key.ToString()+" - "+Theme;
        }

        private WeekThemeKey m_key;
        internal WeekThemeKey Key
        {
            get
            {
                return m_key;
            }
            private set
            {
                m_key = value;
            }
        }



        public ThemeLevel Level
        {
            get
            {
                return Key.Level;
            }
        }
        public ClassType ClassType
        {
            get
            {
                return Key.ClassType;
            }
        }
        public int Week
        {
            get
            {
                return Key.Week;
            }
        }

        private string m_theme;
        public string Theme
        {
            get
            {
                return m_theme;
            }
            private set
            {
                m_theme = value;
            }
        }

        private bool m_attended = false;
        public bool Attended
        {
            get
            {
                return m_attended;
            }
            internal set
            {
                m_attended = value;
            }
        }
    }
}
